#asset_services.py
import logging
from datetime import datetime, date, timedelta
from sqlalchemy import and_, or_, func
from sqlalchemy.orm import joinedload
from werkzeug.utils import secure_filename
import os
import json

from inc.models import db, Asset, AssetCategory, AssetSupplier, AssetMaintenanceLog, AssetAttachment, AssetCustomField, \
    AssetAttribute, AssetAgentConfig, AssetOperationLog, AdminMember, AdminUser

logger = logging.getLogger(__name__)


class AssetService:
    """资产管理服务类"""

    @staticmethod
    def get_asset_categories_tree():
        """获取资产类别树形结构（包含所有类别，无论状态）"""
        try:
            # 获取所有类别并按排序号排序
            all_categories = AssetCategory.query.order_by(AssetCategory.order_num).all()

            # 如果没有类别，返回空列表
            if not all_categories:
                return []

            # 检查是否有任何类别设置了有效的父级ID（非空且非None）
            def has_valid_parent(cat):
                return cat.parent_id is not None and cat.parent_id != ''

            has_parents = any(has_valid_parent(cat) for cat in all_categories)

            if has_parents:
                # 如果有层级结构，构建树形结构
                def build_tree(parent_id=None):
                    tree = []
                    for category in all_categories:
                        # 检查父级ID是否匹配（处理空字符串和None的情况）
                        category_parent = category.parent_id if category.parent_id not in [None, ''] else None
                        if category_parent == parent_id:
                            node = {
                                'id': category.id,
                                'name': category.name,
                                'description': category.description,
                                'order_num': category.order_num,
                                'is_active': category.is_active,
                                'children': build_tree(category.id)
                            }
                            tree.append(node)
                    return tree

                return build_tree()
            else:
                # 如果没有层级结构（所有类别都是顶级），返回扁平结构
                return [{
                    'id': cat.id,
                    'name': cat.name,
                    'description': cat.description,
                    'order_num': cat.order_num,
                    'is_active': cat.is_active,
                    'children': []
                } for cat in all_categories]

        except Exception as e:
            logger.error(f"获取资产类别树失败: {str(e)}")
            return []

    @staticmethod
    def create_asset_category(name, description=None, parent_id=None, order_num=0):
        """创建资产类别"""
        try:
            # 检查名称是否重复
            existing = AssetCategory.query.filter_by(name=name, is_active=True).first()
            if existing:
                return False, "类别名称已存在", None

            category = AssetCategory(
                name=name,
                description=description,
                parent_id=parent_id,
                order_num=order_num
            )
            db.session.add(category)
            db.session.commit()
            return True, "创建成功", category
        except Exception as e:
            db.session.rollback()
            logger.error(f"创建资产类别失败: {str(e)}")
            return False, f"创建失败: {str(e)}", None

    @staticmethod
    def update_asset_category(category_id, name, description=None, parent_id=None, order_num=0, is_active=True):
        """更新资产类别 - 修复版本"""
        try:
            category = AssetCategory.query.get(category_id)
            if not category:
                return False, "类别不存在"

            # 检查名称是否重复（排除当前类别）
            existing = AssetCategory.query.filter(
                AssetCategory.name == name,
                AssetCategory.id != category_id,
                AssetCategory.is_active == True
            ).first()
            if existing:
                return False, "类别名称已存在"

            # 更新字段
            category.name = name
            category.description = description
            category.parent_id = parent_id
            category.order_num = order_num
            category.is_active = is_active
            category.updated_at = datetime.now()

            db.session.commit()
            return True, "更新成功"
        except Exception as e:
            db.session.rollback()
            logger.error(f"更新资产类别失败: {str(e)}")
            return False, f"更新失败: {str(e)}"

    @staticmethod
    def get_assets_with_filters(filters=None, page=1, per_page=20):
        """获取资产列表（带过滤）"""
        try:
            query = Asset.query.options(
                joinedload(Asset.category),
                joinedload(Asset.assigned_to),
                joinedload(Asset.suppliers)
            )

            if filters:
                # 资产名称或编号搜索
                if filters.get('keyword'):
                    keyword = f"%{filters['keyword']}%"
                    query = query.filter(
                        or_(
                            Asset.name.ilike(keyword),
                            Asset.asset_number.ilike(keyword),
                            Asset.serial_number.ilike(keyword),
                            Asset.model.ilike(keyword)
                        )
                    )

                # 类别过滤
                if filters.get('category_id'):
                    # 包括子类别
                    category = AssetCategory.query.get(filters['category_id'])
                    if category:
                        category_ids = category.get_all_children_ids()
                        query = query.filter(Asset.category_id.in_(category_ids))

                # 状态过滤
                if filters.get('status'):
                    query = query.filter(Asset.status == filters['status'])

                # 确认状态过滤
                if filters.get('confirmation_status'):
                    query = query.filter(Asset.confirmation_status == filters['confirmation_status'])

                # 分配人员过滤
                if filters.get('assigned_to_id'):
                    query = query.filter(Asset.assigned_to_id == filters['assigned_to_id'])

                # 自动发现过滤
                if filters.get('auto_discovered') is not None:
                    query = query.filter(Asset.auto_discovered == bool(filters['auto_discovered']))

                # 供应商过滤
                if filters.get('supplier_id'):
                    query = query.filter(Asset.suppliers.any(id=filters['supplier_id']))

            # 排序
            query = query.order_by(Asset.created_at.desc())

            # 分页
            pagination = query.paginate(
                page=page,
                per_page=per_page,
                error_out=False
            )

            return True, "获取成功", pagination
        except Exception as e:
            logger.error(f"获取资产列表失败: {str(e)}")
            return False, f"获取失败: {str(e)}", None

    @staticmethod
    def create_asset(asset_data, custom_fields=None, created_by_id=None):
        """创建资产"""
        try:
            # 检查资产编号是否重复
            existing = Asset.query.filter_by(asset_number=asset_data['asset_number']).first()
            if existing:
                return False, "资产编号已存在", None

            # 检查序列号是否重复（如果提供）
            if asset_data.get('serial_number'):
                existing_serial = Asset.query.filter_by(serial_number=asset_data['serial_number']).first()
                if existing_serial:
                    return False, "序列号已存在", None

            # 处理数值字段，将空字符串转换为 None
            def safe_float(value):
                if value is None or value == '':
                    return None
                try:
                    return float(value)
                except (ValueError, TypeError):
                    return None

            def safe_int(value):
                if value is None or value == '':
                    return None
                try:
                    return int(value)
                except (ValueError, TypeError):
                    return None

            # 处理购买价格
            purchase_price = safe_float(asset_data.get('purchase_price'))
            depreciation_rate = safe_float(asset_data.get('depreciation_rate'))
            warranty_months = safe_int(asset_data.get('warranty_months'))
            # 处理日期字段，将空字符串转换为None
            purchase_date = asset_data.get('purchase_date')
            if purchase_date == '':
                purchase_date = None

            # 设置默认状态为草稿
            status = asset_data.get('status', Asset.DRAFT)

            asset = Asset(
                name=asset_data['name'],
                asset_number=asset_data['asset_number'],
                category_id=asset_data['category_id'],
                model=asset_data.get('model'),
                brand=asset_data.get('brand'),
                serial_number=asset_data.get('serial_number'),
                purchase_price=purchase_price,  # 使用处理后的值
                purchase_date=purchase_date,
                warranty_months=warranty_months,  # 使用处理后的值
                depreciation_rate=depreciation_rate,  # 使用处理后的值
                assigned_to_id=asset_data.get('assigned_to_id'),
                location=asset_data.get('location'),
                status=status,  # 使用处理后的状态
                description=asset_data.get('description'),
                notes=asset_data.get('notes'),
                created_by_id=created_by_id,
                discovery_method='manual',
                cpu_info=asset_data.get('cpu_info'),
                memory_info=asset_data.get('memory_info'),
                disk_info=asset_data.get('disk_info'),
                os_info=asset_data.get('os_info'),
                network_info=asset_data.get('network_info'),
                specifications=asset_data.get('specifications')
            )

            # 处理供应商关联
            if asset_data.get('supplier_ids'):
                suppliers = AssetSupplier.query.filter(AssetSupplier.id.in_(asset_data['supplier_ids'])).all()
                asset.suppliers.extend(suppliers)

            # 计算保修截止日期
            if asset.purchase_date and asset.warranty_months:
                from dateutil.relativedelta import relativedelta
                asset.warranty_expiry = asset.purchase_date + relativedelta(months=asset.warranty_months)

            # 计算当前价值
            if asset.purchase_price and asset.depreciation_rate:
                asset.current_value = asset.calculate_depreciation()

            db.session.add(asset)
            db.session.flush()

            # 处理自定义字段
            if custom_fields:
                for field_id, value in custom_fields.items():
                    if value:  # 只保存有值的字段
                        attribute = AssetAttribute(
                            asset_id=asset.id,
                            field_id=field_id,
                            attribute_value=value
                        )
                        db.session.add(attribute)

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=created_by_id,
                operation_type=AssetOperationLog.OP_CREATE,
                operation_details=json.dumps({
                    'asset_number': asset.asset_number,
                    'name': asset.name
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "创建成功", asset
        except Exception as e:
            db.session.rollback()
            logger.error(f"创建资产失败: {str(e)}")
            return False, f"创建失败: {str(e)}", None

    @staticmethod
    def update_asset(asset_id, update_data, custom_fields=None, updated_by_id=None):
        """更新资产信息"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            # 处理数值字段，将空字符串转换为 None
            def safe_float(value):
                if value is None or value == '':
                    return None
                try:
                    return float(value)
                except (ValueError, TypeError):
                    return None

            def safe_int(value):
                if value is None or value == '':
                    return None
                try:
                    return int(value)
                except (ValueError, TypeError):
                    return None

            # 处理数值字段
            if 'purchase_price' in update_data:
                update_data['purchase_price'] = safe_float(update_data['purchase_price'])
            if 'depreciation_rate' in update_data:
                update_data['depreciation_rate'] = safe_float(update_data['depreciation_rate'])
            if 'warranty_months' in update_data:
                update_data['warranty_months'] = safe_int(update_data['warranty_months'])
            # 处理日期字段
            if 'purchase_date' in update_data:
                if update_data['purchase_date'] == '':
                    update_data['purchase_date'] = None
                elif isinstance(update_data['purchase_date'], str):
                    from datetime import datetime
                    try:
                        update_data['purchase_date'] = datetime.strptime(update_data['purchase_date'], '%Y-%m-%d').date()
                    except ValueError:
                        update_data['purchase_date'] = None

            # 检查资产编号是否重复（排除自己）
            if 'asset_number' in update_data:
                existing = Asset.query.filter(
                    Asset.asset_number == update_data['asset_number'],
                    Asset.id != asset_id
                ).first()
                if existing:
                    return False, "资产编号已存在"

            # 检查序列号是否重复（排除自己）
            if 'serial_number' in update_data and update_data['serial_number']:
                existing_serial = Asset.query.filter(
                    Asset.serial_number == update_data['serial_number'],
                    Asset.id != asset_id
                ).first()
                if existing_serial:
                    return False, "序列号已存在"

            # 记录变更前的数据
            old_data = {
                'name': asset.name,
                'asset_number': asset.asset_number,
                'status': asset.status
            }

            # 更新字段
            hardware_fields = ['cpu_info', 'memory_info', 'disk_info', 'os_info', 'network_info', 'specifications']
            for field in hardware_fields:
                if field in update_data:
                    setattr(asset, field, update_data[field])

            for key, value in update_data.items():
                if hasattr(asset, key) and key not in ['id', 'created_at', 'created_by_id']:
                    setattr(asset, key, value)

            # 处理供应商更新
            if 'supplier_ids' in update_data:
                asset.suppliers = []
                if update_data['supplier_ids']:
                    suppliers = AssetSupplier.query.filter(AssetSupplier.id.in_(update_data['supplier_ids'])).all()
                    asset.suppliers.extend(suppliers)

            # 处理自定义字段
            if custom_fields is not None:
                # 删除旧的属性
                AssetAttribute.query.filter_by(asset_id=asset_id).delete()

                # 添加新的属性
                for field_id, value in custom_fields.items():
                    if value:  # 只保存有值的字段
                        attribute = AssetAttribute(
                            asset_id=asset.id,
                            field_id=field_id,
                            attribute_value=value
                        )
                        db.session.add(attribute)

            # 重新计算相关字段
            if asset.purchase_date and asset.warranty_months:
                from dateutil.relativedelta import relativedelta
                asset.warranty_expiry = asset.purchase_date + relativedelta(months=asset.warranty_months)

            if asset.purchase_price and asset.depreciation_rate:
                asset.current_value = asset.calculate_depreciation()

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=updated_by_id,
                operation_type=AssetOperationLog.OP_UPDATE,
                operation_details=json.dumps({
                    'old_data': old_data,
                    'new_data': {
                        'name': asset.name,
                        'asset_number': asset.asset_number,
                        'status': asset.status
                    }
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "更新成功", asset
        except Exception as e:
            db.session.rollback()
            logger.error(f"更新资产失败: {str(e)}")
            return False, f"更新失败: {str(e)}", None

    @staticmethod
    def change_asset_status(asset_id, new_status, notes=None, changed_by_id=None):
        """变更资产状态"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            old_status = asset.status
            asset.status = new_status
            if notes:
                asset.notes = notes

            # 如果是分配状态，记录分配日期
            if new_status == Asset.IN_USE and asset.assigned_to_id:
                asset.assigned_date = date.today()

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=changed_by_id,
                operation_type=AssetOperationLog.OP_UPDATE,
                operation_details=json.dumps({
                    'field': 'status',
                    'old_value': old_status,
                    'new_value': new_status,
                    'notes': notes
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "状态变更成功", asset
        except Exception as e:
            db.session.rollback()
            logger.error(f"变更资产状态失败: {str(e)}")
            return False, f"状态变更失败: {str(e)}", None

    @staticmethod
    def assign_asset_to_user(asset_id, user_id, assign_date=None, notes=None, current_user_id=None):
        """分配资产给指定人员"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            user = AdminMember.query.get(user_id)
            if not user:
                return False, "人员不存在"

            # 记录旧值
            old_assigned_to = asset.assigned_to_id
            old_status = asset.status

            # 更新资产信息
            asset.assigned_to_id = user_id
            asset.assigned_date = datetime.strptime(assign_date, '%Y-%m-%d').date() if assign_date else date.today()
            asset.status = Asset.IN_USE
            if notes:
                asset.notes = notes

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=current_user_id,
                operation_type=AssetOperationLog.OP_ASSIGN,
                operation_details=json.dumps({
                    'old_assigned_to': old_assigned_to,
                    'new_assigned_to': user_id,
                    'old_status': old_status,
                    'new_status': Asset.IN_USE,
                    'assign_date': asset.assigned_date.strftime('%Y-%m-%d'),
                    'notes': notes
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "分配成功"
        except Exception as e:
            db.session.rollback()
            logger.error(f"分配资产失败: {str(e)}")
            return False, f"分配失败: {str(e)}"

    @staticmethod
    def confirm_asset(asset_id, user_id, notes=None):
        """确认资产"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            asset.confirm_asset(user_id, notes)

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=user_id,
                operation_type=AssetOperationLog.OP_CONFIRM,
                operation_details=json.dumps({
                    'notes': notes
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "资产确认成功"
        except Exception as e:
            db.session.rollback()
            logger.error(f"确认资产失败: {str(e)}")
            return False, f"确认失败: {str(e)}"

    @staticmethod
    def reject_asset(asset_id, user_id, reason):
        """拒绝资产"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            asset.reject_asset(user_id, reason)

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=user_id,
                operation_type=AssetOperationLog.OP_REJECT,
                operation_details=json.dumps({
                    'reason': reason
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "资产已拒绝"
        except Exception as e:
            db.session.rollback()
            logger.error(f"拒绝资产失败: {str(e)}")
            return False, f"拒绝失败: {str(e)}"

    @staticmethod
    def create_maintenance_log(asset_id, log_data, performed_by_id):
        """创建维护记录"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            # 处理空值
            def safe_float(value):
                if value is None or value == '':
                    return None
                try:
                    return float(value)
                except (ValueError, TypeError):
                    return None

            maintenance_log = AssetMaintenanceLog(
                asset_id=asset_id,
                maintenance_type=log_data['maintenance_type'],
                title=log_data['title'],
                description=log_data.get('description'),
                performed_by_id=performed_by_id,
                maintenance_date=datetime.fromisoformat(log_data.get('maintenance_date')) if log_data.get(
                    'maintenance_date') else datetime.now(),
                next_maintenance_date=datetime.fromisoformat(
                    log_data.get('next_maintenance_date')).date() if log_data.get('next_maintenance_date') else None,
                cost=safe_float(log_data.get('cost')),
                duration_hours=safe_float(log_data.get('duration_hours')),
                parts_replaced=json.dumps(log_data.get('parts_replaced', [])),
                status=log_data.get('status', AssetMaintenanceLog.STATUS_COMPLETED)
            )

            # 如果是维护中，更新资产状态
            if log_data['maintenance_type'] in [AssetMaintenanceLog.REPAIR, AssetMaintenanceLog.MAINTENANCE]:
                asset.status = Asset.MAINTENANCE

            db.session.add(maintenance_log)

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset_id,
                operator_id=performed_by_id,
                operation_type=AssetOperationLog.OP_MAINTENANCE,
                operation_details=json.dumps({
                    'maintenance_id': maintenance_log.id,
                    'title': maintenance_log.title,
                    'type': maintenance_log.maintenance_type
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "维护记录创建成功", maintenance_log
        except Exception as e:
            db.session.rollback()
            logger.error(f"创建维护记录失败: {str(e)}")
            return False, f"创建失败: {str(e)}", None

    @staticmethod
    def get_asset_statistics():
        """获取资产统计信息"""
        try:
            total_assets = Asset.query.count()
            total_value = db.session.query(func.sum(Asset.current_value)).scalar() or 0

            status_stats = db.session.query(
                Asset.status,
                func.count(Asset.id)
            ).group_by(Asset.status).all()

            confirmation_stats = db.session.query(
                Asset.confirmation_status,
                func.count(Asset.id)
            ).group_by(Asset.confirmation_status).all()

            category_stats = db.session.query(
                AssetCategory.name,
                func.count(Asset.id)
            ).join(Asset, Asset.category_id == AssetCategory.id) \
                .group_by(AssetCategory.name).all()

            # 即将过保的资产
            expiring_soon = Asset.query.filter(
                Asset.warranty_expiry.isnot(None),
                Asset.warranty_expiry <= date.today() + timedelta(days=30),
                Asset.warranty_expiry >= date.today()
            ).count()

            # 需要维护的资产
            need_maintenance = AssetMaintenanceLog.query.filter(
                AssetMaintenanceLog.next_maintenance_date.isnot(None),
                AssetMaintenanceLog.next_maintenance_date <= date.today() + timedelta(days=7)
            ).count()

            # 自动发现的资产
            auto_discovered = Asset.query.filter_by(auto_discovered=True).count()

            return {
                'total_assets': total_assets,
                'total_value': float(total_value),
                'status_distribution': dict(status_stats),
                'confirmation_distribution': dict(confirmation_stats),
                'category_distribution': dict(category_stats),
                'expiring_soon': expiring_soon,
                'need_maintenance': need_maintenance,
                'auto_discovered': auto_discovered
            }
        except Exception as e:
            logger.error(f"获取资产统计失败: {str(e)}")
            return {}

    @staticmethod
    def process_agent_report(host_identifier, asset_name, specifications, agent_id, agent_version, report_time):
        """处理Agent上报数据 - 完整字段修正版本"""
        try:
            # 首先尝试通过host_identifier匹配
            asset = Asset.query.filter_by(host_identifier=host_identifier).first()
            action = "updated"

            if not asset:
                # 如果没有host_identifier匹配，尝试通过序列号匹配
                serial_number = None
                if isinstance(specifications, dict):
                    hw_info = specifications.get('hardware_info', {})
                    serial_number = hw_info.get('serial_number')
                elif isinstance(specifications, str):
                    try:
                        specs_dict = json.loads(specifications)
                        hw_info = specs_dict.get('hardware_info', {})
                        serial_number = hw_info.get('serial_number')
                    except:
                        pass

                if serial_number:
                    asset = Asset.query.filter_by(serial_number=serial_number).first()

                # 如果还没有匹配，尝试通过资产名称和硬件特征匹配
                if not asset and asset_name:
                    same_name_assets = Asset.query.filter(
                        Asset.name.ilike(f"%{asset_name}%")
                    ).all()

                    for candidate in same_name_assets:
                        if candidate.specifications and specifications:
                            try:
                                candidate_specs = json.loads(candidate.specifications) if isinstance(
                                    candidate.specifications, str) else candidate.specifications
                                agent_specs = json.loads(specifications) if isinstance(specifications,
                                                                                       str) else specifications

                                if (candidate_specs.get('hardware_info', {}).get('serial_number') ==
                                        agent_specs.get('hardware_info', {}).get('serial_number')):
                                    asset = candidate
                                    break
                            except:
                                continue

            # 确保specifications是字典格式
            if isinstance(specifications, str):
                try:
                    specifications = json.loads(specifications)
                except:
                    specifications = {}
            elif specifications is None:
                specifications = {}

            hw_info = specifications.get('hardware_info', {})
            system_info = specifications.get('system_info', {})
            network_info = specifications.get('network_info', {})

            if asset:
                # 更新现有资产 - 更新所有硬件信息字段
                asset.specifications = json.dumps(specifications, ensure_ascii=False)
                asset.agent_version = agent_version
                asset.host_identifier = host_identifier
                asset.updated_at = datetime.now()

                # ✅ 更新所有硬件信息字段
                if hw_info.get('cpu'):
                    asset.cpu_info = json.dumps(hw_info['cpu'], ensure_ascii=False)
                if hw_info.get('memory'):
                    asset.memory_info = json.dumps(hw_info['memory'], ensure_ascii=False)
                if hw_info.get('disks'):
                    asset.disk_info = json.dumps(hw_info['disks'], ensure_ascii=False)
                if system_info:
                    asset.os_info = json.dumps(system_info, ensure_ascii=False)
                if network_info:
                    asset.network_info = json.dumps(network_info, ensure_ascii=False)
                # ✅ 新增：处理mac_info
                if hw_info.get('mac_info'):
                    asset.mac_info = json.dumps(hw_info['mac_info'], ensure_ascii=False)
                # ✅ 新增：处理user_info
                if hw_info.get('user_info'):
                    asset.user_info = json.dumps(hw_info['user_info'], ensure_ascii=False)

                if hw_info.get('serial_number'):
                    asset.serial_number = hw_info['serial_number']

                # 如果资产名称为空或为默认名称，更新为上报的名称
                if not asset.name or asset.name.startswith('自动发现-'):
                    asset.name = asset_name or f"自动发现-{host_identifier}"

            else:
                # 创建新资产
                action = "created"
                asset = Asset(
                    name=asset_name or f"自动发现-{host_identifier}",
                    asset_number=f"AUTO_{int(datetime.now().timestamp())}",
                    host_identifier=host_identifier,
                    specifications=json.dumps(specifications, ensure_ascii=False),
                    status=Asset.PENDING,
                    category_id=1,  # 确保默认类别存在
                    created_by_id=1,  # 系统用户
                    agent_id=agent_id,
                    agent_version=agent_version,
                    auto_discovered=True,
                    discovery_method='agent',
                    confirmation_status=Asset.CONFIRMATION_PENDING,
                    created_at=datetime.now(),
                    updated_at=datetime.now()
                )

                # 设置所有硬件信息字段
                if hw_info.get('cpu'):
                    asset.cpu_info = json.dumps(hw_info['cpu'], ensure_ascii=False)
                if hw_info.get('memory'):
                    asset.memory_info = json.dumps(hw_info['memory'], ensure_ascii=False)
                if hw_info.get('disks'):
                    asset.disk_info = json.dumps(hw_info['disks'], ensure_ascii=False)
                if system_info:
                    asset.os_info = json.dumps(system_info, ensure_ascii=False)
                if network_info:
                    asset.network_info = json.dumps(network_info, ensure_ascii=False)
                # ✅ 新增：设置mac_info
                if hw_info.get('mac_info'):
                    asset.mac_info = json.dumps(hw_info['mac_info'], ensure_ascii=False)
                # ✅ 新增：设置user_info
                if hw_info.get('user_info'):
                    asset.user_info = json.dumps(hw_info['user_info'], ensure_ascii=False)

                # 设置序列号
                if hw_info.get('serial_number'):
                    asset.serial_number = hw_info['serial_number']

                db.session.add(asset)

            # 提交数据库变更
            db.session.commit()

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset.id,
                operator_id=1,
                operation_type=AssetOperationLog.OP_AGENT_REPORT,
                operation_details=json.dumps({
                    'action': action,
                    'agent_id': agent_id,
                    'agent_version': agent_version,
                    'host_identifier': host_identifier,
                    'report_time': report_time.isoformat() if hasattr(report_time, 'isoformat') else str(report_time)
                }, ensure_ascii=False),
                created_at=datetime.now()
            )
            db.session.add(log)
            db.session.commit()

            return True, f"资产{action}成功", asset

        except Exception as e:
            db.session.rollback()
            logger.error(f"处理Agent上报失败: {str(e)}", exc_info=True)
            return False, f"处理失败: {str(e)}", None


class AssetBindingService:
    """资产绑定服务"""

    @staticmethod
    def bind_existing_asset(host_identifier, asset_id, user_id):
        """将Agent绑定到现有资产"""
        try:
            # 检查资产是否存在
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            # 检查host_identifier是否已被其他资产使用
            existing = Asset.query.filter_by(host_identifier=host_identifier).first()
            if existing and existing.id != asset_id:
                return False, f"该设备标识已被资产 {existing.asset_number} 使用"

            # 更新绑定信息
            old_host_identifier = asset.host_identifier
            asset.host_identifier = host_identifier
            asset.auto_discovered = False  # 手动绑定的资产不算自动发现
            asset.updated_at = datetime.now()

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset_id,
                operator_id=user_id,
                operation_type=AssetOperationLog.OP_UPDATE,
                operation_details=json.dumps({
                    'field': 'host_identifier',
                    'old_value': old_host_identifier,
                    'new_value': host_identifier,
                    'action': 'bind'
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "绑定成功"

        except Exception as e:
            db.session.rollback()
            logger.error(f"资产绑定失败: {str(e)}")
            return False, f"绑定失败: {str(e)}"

    @staticmethod
    def unbind_asset(asset_id, user_id):
        """解除资产绑定"""
        try:
            asset = Asset.query.get(asset_id)
            if not asset:
                return False, "资产不存在"

            old_host_identifier = asset.host_identifier
            asset.host_identifier = None
            asset.agent_id = None
            asset.updated_at = datetime.now()

            # 记录操作日志
            log = AssetOperationLog(
                asset_id=asset_id,
                operator_id=user_id,
                operation_type=AssetOperationLog.OP_UPDATE,
                operation_details=json.dumps({
                    'field': 'host_identifier',
                    'old_value': old_host_identifier,
                    'new_value': None,
                    'action': 'unbind'
                })
            )
            db.session.add(log)

            db.session.commit()
            return True, "解绑成功"

        except Exception as e:
            db.session.rollback()
            logger.error(f"资产解绑失败: {str(e)}")
            return False, f"解绑失败: {str(e)}"


class AssetImportExportService:
    """资产导入导出服务"""

    @staticmethod
    def export_assets_to_excel(filters=None):
        """导出资产到Excel"""
        try:
            import pandas as pd
            from io import BytesIO

            success, message, result = AssetService.get_assets_with_filters(filters, page=1, per_page=10000)
            if not success:
                return False, message

            assets_data = []
            for asset in result.items:
                assets_data.append({
                    '资产编号': asset.asset_number,
                    '资产名称': asset.name,
                    '类别': asset.category.name if asset.category else '',
                    '型号': asset.model or '',
                    '品牌': asset.brand or '',
                    '序列号': asset.serial_number or '',
                    '状态': asset.status_label,
                    '确认状态': asset.confirmation_status_label,
                    'CPU': asset.cpu_info or '',
                    '内存': asset.memory_info or '',
                    '硬盘': asset.disk_info or '',
                    '操作系统': asset.os_info or '',
                    '网络信息': asset.network_info or '',
                    '使用人': asset.assigned_to.name if asset.assigned_to else '',
                    '位置': asset.location or '',
                    '购买价格': float(asset.purchase_price) if asset.purchase_price else 0,
                    '当前价值': float(asset.current_value) if asset.current_value else 0,
                    '购买日期': asset.purchase_date.strftime('%Y-%m-%d') if asset.purchase_date else '',
                    '保修截止': asset.warranty_expiry.strftime('%Y-%m-%d') if asset.warranty_expiry else '',
                    '自动发现': '是' if asset.auto_discovered else '否',
                    '设备标识': asset.host_identifier or ''
                })

            df = pd.DataFrame(assets_data)
            output = BytesIO()
            with pd.ExcelWriter(output, engine='openpyxl') as writer:
                df.to_excel(writer, sheet_name='资产清单', index=False)

            output.seek(0)
            return True, "导出成功", output
        except Exception as e:
            logger.error(f"导出资产失败: {str(e)}")
            return False, f"导出失败: {str(e)}", None


    @staticmethod
    def import_assets_from_excel(file_data, created_by_id, update_existing=False):
        """从Excel导入资产 - 支持更新模式"""
        try:
            import pandas as pd

            df = pd.read_excel(file_data)
            required_columns = ['资产编号', '资产名称', '类别']
            missing_columns = [col for col in required_columns if col not in df.columns]

            if missing_columns:
                return False, f"缺少必要列: {', '.join(missing_columns)}", []

            success_count = 0
            error_messages = []

            for index, row in df.iterrows():
                try:
                    # 查找类别
                    category = AssetCategory.query.filter_by(name=row['类别']).first()
                    if not category:
                        error_messages.append(f"第{index + 2}行: 类别 '{row['类别']}' 不存在")
                        continue

                    asset_data = {
                        'asset_number': str(row['资产编号']),
                        'name': row['资产名称'],
                        'category_id': category.id,
                        'model': row.get('型号', ''),
                        'brand': row.get('品牌', ''),
                        'serial_number': str(row.get('序列号', '')) if pd.notna(row.get('序列号')) else None,
                        'location': row.get('位置', ''),
                        'purchase_price': float(row['购买价格']) if pd.notna(row.get('购买价格')) else None,
                        'status': Asset.AVAILABLE
                    }

                    # 处理使用人
                    if pd.notna(row.get('使用人')):
                        member = AdminMember.query.filter_by(name=row['使用人']).first()
                        if member:
                            asset_data['assigned_to_id'] = member.id

                    # 处理购买日期
                    if pd.notna(row.get('购买日期')):
                        try:
                            if isinstance(row['购买日期'], str):
                                asset_data['purchase_date'] = datetime.strptime(row['购买日期'], '%Y-%m-%d').date()
                            else:
                                asset_data['purchase_date'] = row['购买日期'].date()
                        except Exception as e:
                            error_messages.append(f"第{index + 2}行: 购买日期格式错误 - {str(e)}")
                            continue

                    # 检查资产是否已存在
                    existing_asset = Asset.query.filter_by(asset_number=asset_data['asset_number']).first()

                    if existing_asset and update_existing:
                        # 更新现有资产
                        success, message, asset = AssetService.update_asset(
                            existing_asset.id, asset_data, None, created_by_id
                        )
                        action = "更新"
                    else:
                        # 创建新资产
                        success, message, asset = AssetService.create_asset(asset_data, None, created_by_id)
                        action = "创建"

                    if success:
                        success_count += 1
                    else:
                        error_messages.append(f"第{index + 2}行: {action}失败 - {message}")

                except Exception as e:
                    error_messages.append(f"第{index + 2}行: 处理失败 - {str(e)}")

            return True, f"导入完成: 成功{success_count}条, 失败{len(error_messages)}条", error_messages
        except Exception as e:
            logger.error(f"导入资产失败: {str(e)}")
            return False, f"导入失败: {str(e)}", []
